﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Diagnostics;
using Engine.Dtd.Schema;
using System.Collections.Concurrent;

namespace Engine.Sgml {
    /// <summary>
    /// This stack maintains a high water mark for allocated objects so the client
    /// can reuse the objects in the stack to reduce memory allocations.
    /// </summary>
    [DebuggerNonUserCode]
    [DebuggerTypeProxy(typeof(CollectionDebugView<>))]
    [DebuggerDisplay("Count = {Count} Size = {items.Length}")]
    internal class HwStack<T> : IEnumerable<T>, ICollection where T : class {
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private static T[] emptyArray = new T[0];
        protected T[] items;
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private int size;
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private int growthSize;
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private Func<T> constructor;

        /// <summary>
        /// Initializes a new instance of the HWStack class.
        /// </summary>
        /// <param name="capacity">The amount to grow the stack space by, if more space is needed on the stack.</param>
        /// <param name="generator">A method that create and returns a T object.</param>
        public HwStack(int capacity, Func<T> generator) {
            Guard.ArgumentNotNull(generator, "generator");
            growthSize = capacity;
            items = emptyArray;
            constructor = generator;
            }

        /// <summary>
        /// The number of items currently in the stack.
        /// </summary>
        public int Count {
            get { return size; }
            }

        public void Clear() {
            for (int i = 0; i < items.Length; i++)
                items[i] = default(T);
            size = 0;
            }

        private T Construct(T node) {
            if (node == null)
                node = constructor();
            return node;
            }

        /// <summary>
        /// The size (capacity) of the stack.
        /// </summary>
        public int Capacity {
            get { return items.Length; }
            }

        public T LastElement {
            get {
                if (Count == 0)
                    return default(T);
                else
                    return items[Count - 1];
                }
            }

        public T FirstElement {
            get {
                if (Count == 0)
                    return default(T);
                else
                    return items[0];
                }
            }

        /// <summary>
        /// Removes and returns the item at the top of the stack
        /// </summary>
        /// <returns>The item at the top of the stack.</returns>
        public void Pop() {
            if (size == 0)
                return;
            size--;
            T item = items[size];
            // OnRemoveElement(item, size);
            }

        /// <summary>
        /// Pushes a new slot at the top of the stack.
        /// </summary>
        /// <returns>The object at the top of the stack.</returns>
        /// <remarks>This method tries to reuse a slot.</remarks>
        public T Push() {
            if (size == items.Length)
                GrowthStack();
            T node = Construct(items[size]);
            OnInsertElement(node, size);
            size++;
            return node;
            }

        public void Insert(T element, int index) {
            if (index < 0 || index > size)
                throw new ArgumentOutOfRangeException("index", "The value must be more than or equal zero and less than count of elements.");
            if (size + 1 > items.Length)
                GrowthStack();
            // Выполнить сдвиг элементов в списке
            // Увеличить индекс сдвигаемого элемента
            for (int i = size - 1; i >= index; i--)
                OnMoveElement(items[i], i + 1);
            OnInsertElement(element, index);
            size++;
            }

        public T RemoveAt(int index) {
            if (index < 0 || index >= size)
                throw new ArgumentOutOfRangeException("index", "The value must be more than or equal zero and less than count of elements.");
            T element = items[index];
            OnRemoveElement(element, index);
            for (int i = index + 1; i < size; i++)
                OnMoveElement(items[i], i - 1);
            size--;
            items[size] = default(T);
            return element;
            }

        public void Move(int itemIndex, int position) {
            if (position < 0 || position >= Count)
                throw new ArgumentOutOfRangeException("position", "The value must be more than or equal zero and less than count of elements.");
            if (itemIndex == position)
                return;
            T element = items[itemIndex];
            if (itemIndex < position)
                for (int i = itemIndex + 1; i <= position; i++)
                    OnMoveElement(items[i], i - 1);
            else
                for (int i = itemIndex - 1; i >= position; i--)
                    OnMoveElement(items[i], i + 1);
            OnInsertElement(element, position);
            }

        protected virtual void OnInsertElement(T element, int index) {
            items[index] = element;
            }

        protected virtual void OnMoveElement(T element, int index) {
            items[index] = element;
            }

        protected virtual void OnRemoveElement(T element, int index) {
            items[index] = default(T);
            }

        public void Add(T item) {
            if (size == items.Length)
                GrowthStack();
            OnInsertElement(item, size);
            size++;
            }

        /// <summary>
        /// Returns the item at the specified index or null if index is out of bounds
        /// </summary>
        /// <param name="index">The index of the item to retrieve.</param>
        /// <returns>The item at the requested index or null if index is out of bounds.</returns>
        public T this[int index] {
            get { return (index >= 0 && index < Count) ? items[index] : default(T); }
            }

        /// <summary>
        /// Gets the item at the specified index from internal collection, or null if it does not exist.
        /// </summary>
        /// <param name="index">The index of the item to retrieve.</param>
        /// <returns>The item at the specified index or null if index is out of bounds.</returns>
        internal T GetOrDefault(int index) {
            if (index >= 0 && index < items.Length)
                return items[index];
            else
                return default(T);
            }

        private void GrowthStack() {
            GrowthStack(items.Length + growthSize);
            }

        private void GrowthStack(int newsize) {
            T[] array = new T[newsize];
            Array.Copy(items, array, size);
            items = array;
            }

        public IEnumerator<T> GetEnumerator() {
            return items.Take(size).GetEnumerator();
            }

        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
            }

        public void CopyTo(Array array, int index) {
            Array.Copy(items, index, array, 0, Count);
            }

        public bool IsSynchronized {
            get { return false; }
            }

        public object SyncRoot {
            get { return null; }
            }
        }
    }